/**
 * Makes the first character of a string uppercase
 */
export function upperFirst(str: string): string {
	return str.charAt(0).toUpperCase() + str.slice(1);
}

interface HTMLExpandElement extends HTMLElement {
	_parent?: (Node & ParentNode & HTMLElement) | null;
	_initialStyle: {
		transition: string;
		overflow: string | null;
		height?: string | null;
		width?: string | null;
	};
}

export default function (expandedParentClass = '', x = false) {
	const sizeProperty = x ? 'width' : ('height' as 'width' | 'height');
	const offsetProperty = `offset${upperFirst(sizeProperty)}` as 'offsetHeight' | 'offsetWidth';

	return {
		beforeEnter(el: HTMLExpandElement) {
			el._parent = el.parentNode as (Node & ParentNode & HTMLElement) | null;
			el._initialStyle = {
				transition: el.style.transition,
				overflow: el.style.overflow,
				[sizeProperty]: el.style[sizeProperty],
			};
		},

		enter(el: HTMLExpandElement) {
			const initialStyle = el._initialStyle;

			el.style.setProperty('transition', 'none', 'important');
			el.style.overflow = 'hidden';
			// const offset = `${el[offsetProperty]}px`;

			// el.style[sizeProperty] = '0';

			void el.offsetHeight; // force reflow

			el.style.transition = initialStyle.transition;

			if (expandedParentClass && el._parent) {
				el._parent.classList.add(expandedParentClass);
			}

			requestAnimationFrame(() => {
				// el.style[sizeProperty] = offset;
			});
		},

		afterEnter: resetStyles,
		enterCancelled: resetStyles,

		leave(el: HTMLExpandElement) {
			el._initialStyle = {
				transition: '',
				overflow: el.style.overflow,
				[sizeProperty]: el.style[sizeProperty],
			};

			el.style.overflow = 'hidden';
			el.style[sizeProperty] = `${el[offsetProperty]}px`;
			/* eslint-disable-next-line */
			void el.offsetHeight; // force reflow

			requestAnimationFrame(() => (el.style[sizeProperty] = '0'));
		},

		afterLeave,
		leaveCancelled: afterLeave,
	};

	function afterLeave(el: HTMLExpandElement) {
		if (expandedParentClass && el._parent) {
			el._parent.classList.remove(expandedParentClass);
		}
		resetStyles(el);
	}

	function resetStyles(el: HTMLExpandElement) {
		const size = el._initialStyle[sizeProperty];
		el.style.overflow = el._initialStyle.overflow!;
		if (size != null) el.style[sizeProperty] = size;
		Reflect.deleteProperty(el, '_initialStyle');
	}
}
